/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package poproject.Common;

import JaCoP.core.Var;
import JaCoP.search.Search;
import JaCoP.search.SolutionListener;
import java.util.*;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import poproject.CombinatorialTask.Word;
import poproject.PlanningTask.FootballTeam;

/** @class DataConverter
 * 
 * Klasa umożliwiająca konwersja danych rozwiązania JaCoPa na sposób wymagany
 * do reprezentacji w GUI.
 *
 * @author Jan Karaś
 */
public class DataConverter {
    
    // ********************** CONVERTER FOR COMBINATORIAL TASK ********************** //
    /**
     * Konwersja tekstu.
     * Metoda konwertująca tekst jaki wprowadzi użytkownik na postać zrozumiałą 
     * dla silnika aplikacji.
     * 
     * @param userText Tekst wprowadzony przez użytkownika
     * @return Lista słów
     */
    public static ArrayList<Word> stringToWordArray(String userText)
    {
        ArrayList<Word> mWordList = new ArrayList<>();
        String[] lines  = userText.split("\\r\n");
        
        for(int i=0; i<lines.length; ++i)
            mWordList.add(new Word(lines[i]));
        
        return mWordList;        
    }
        
    /**
     * Pobranie rozwiązania w postaci tekstu.
     * Metoda umożliwia zbudowanie pełnego rozwiązania problemu na podstawie
     * danych pochodzących z metody poszukiwań oraz listy słów.
     * @param search Metoda poszukiwań
     * @param wordList  Lista słów
     * @return 
     */
    public static String getSolutionString(Search search, ArrayList<Word> wordList)
    {
        StringBuilder buf = new StringBuilder();
        SolutionListener sl = search.getSolutionListener();
        
        HashMap<String,String> hm = convertSolution(sl.getVariables());
        
        findWordValue(hm, wordList);
        
        String equation = getEquation(wordList);
        
        buf.append("Rozwiązanie:\n").append(sl.toString().substring(12));
        buf.append("Węzły:\t\t").append(search.getNodes()).append("\n");
        buf.append("Decyzje:\t\t").append(search.getDecisions()).append("\n");
        buf.append("Złe decyzje:\t\t").append(search.getWrongDecisions()).append("\n");
        buf.append("Nawroty:\t\t").append(search.getBacktracks()).append("\n");
        buf.append("Max głębokość:\t").append(search.getMaximumDepth()).append("\n\n");
        buf.append("Rozwiązanie liczbowe to:\n\n");
        buf.append(equation);
        
        return buf.toString();
    }
    
    /**
     * Konwersja danych.
     * Metoda umożliwia konwersję danych pochodzących z poszukiwań rozwiązania 
     * na HashMape przechowującą literę oraz odpowiadającą jej wartość liczbową.
     * @param solutionVariables
     * @return 
     */
    private static HashMap<String,String> convertSolution(Var[] solutionVariables)
    {
        HashMap<String,String> hm = new HashMap<>();
        
        for(Var v :solutionVariables)
        {
            String str = v.toStringFull();
            hm.put(str.substring(0, 1), str.substring(1, 2));
        }        
        return hm;
    }
    
    /**
     * Wyznaczenie wartości słów.
     * Metoda umożliwiająca wyznaczenie wartości poszczególnych słów na podstawie
     * informacji o relacji litera <-> wartość oraz informacji o wprowadzonych
     * słowach.
     * @param hm Mapa relacji litera <-> wartość
     * @param wordList Lista wprowadzonych słów
     */
    private static void findWordValue(HashMap<String,String> hm, ArrayList<Word> wordList)
    {
        for(Word word : wordList)
        {
            String wordValueStr = "";
            String str = word.getWord();
            for(int i=0; i<str.length(); i++)
            {
                String letter = Character.toString(str.charAt(i));
                if(hm.containsKey(letter))
                    wordValueStr += hm.get(letter);               
            }
            word.setWordValue(Integer.parseInt(wordValueStr));
        }
    }
    
    /**
     * Równanie liczbowe.
     * Metoda pozwalająca na wyznaczenie równania liczbowego na podstawie listy 
     * słów z uzupełnionymi wartościami słów które są dostępne dopiero po dokonaniu poszukiwań.
     * @param wordList Lista wprowadzonych słów
     * @return Łańcuch znaków z równaniem gotowym do wyświetlenia.
     */
    private static String getEquation(ArrayList<Word> wordList)
    {        
        String outStr = "";
        for(Word w : wordList)
        {
            
            switch(w.getWordSign())
            {
                case Plus:
                    outStr += "+" + w.getWordValue().toString() + "\n";
                    break;
                case Minus:
                    outStr += "-" + w.getWordValue().toString() + "\n";
                    break;
                case Equal:
                    outStr += "=" + w.getWordValue().toString() + "\n";
                    break;
                case Unknown:
                    outStr += w.getWordValue().toString() + "\n";
                    break;
                default:
                    break;
            }
            
        }
        return outStr;
    }
    // ********************** CONVERTER FOR COMBINATORIAL TASK ********************** //
    
    // ********************** CONVERTER FOR PLANNING TASK ********************** //
    /**
     * Konwersja danych GUI na dane dla JaCoPa (drużyny)
     * @param teamTable Tablica danych wprowadzona przez użytkownika w GUI
     * @return Lista drużyn biorąca udział w trutnieju
     */
    public static ArrayList<FootballTeam> tableToTeamArray(JTable teamTable)
    {
        ArrayList<FootballTeam> teamList = new ArrayList<>();
        DefaultTableModel dtm = (DefaultTableModel)teamTable.getModel();

        for(int row = 0; row < dtm.getRowCount(); row++)
        {
            String teamName = (String)dtm.getValueAt(row, 0);
            String fieldName = (String)dtm.getValueAt(row, 1);
            Integer matchNo = (Integer)dtm.getValueAt(row, 2);
            teamList.add(new FootballTeam(teamName, matchNo, fieldName));
        }
        
        return teamList;
    }
        
    /**
     * Lista rozgrywek.
     * Lista możliwych meczy do rozegrania w tutrnieju.
     * @param teamsArray Lista drużyn
     * @return Lista spotkań
     */
    public static ArrayList<String> teamArrayToFootbalGamesArray(ArrayList<FootballTeam> teamsArray)
    {
        ArrayList<String> gamesArray = new ArrayList<>();
        
        for(int i=0; i<teamsArray.size(); i++)
        {
            FootballTeam teamA = teamsArray.get(i);
            for(int j=i+1; j<teamsArray.size(); j++)
            {
                FootballTeam teamB = teamsArray.get(j);
                gamesArray.add(teamA.getName() + " vs. " + teamB.getName());
            }            
        }        
        return gamesArray;
    }
    

    /**
     * Wyznaczenie rozwiązania dla problemu planowania.
     * Przetworzenie danych pochodzących z jacopa na dane, które można umieścić w tabeli rozwiązań
     * @param search Metoda poszukiwań
     * @param scheduleTableRound1 Tabela rozwiązań dla rundy 1
     * @param scheduleTableRound2 Tabela rozwiązań dla rundy 2
     * @param teamsArray Lista zespołów biorących udział w turnieju
     */
    public static void PlanningTaskSolution(Search search, JTable scheduleTableRound1, JTable scheduleTableRound2, ArrayList<FootballTeam> teamsArray)
    {
        DefaultTableModel solutionTableRound1Model = (DefaultTableModel)scheduleTableRound1.getModel();     // Model tabeli rozwiązania PT runda 1
        DefaultTableModel solutionTableRound2Model = (DefaultTableModel)scheduleTableRound2.getModel();     // Model tabeli rozwiązania PT runda 2
        SolutionListener sl = search.getSolutionListener();     // Rozwiązanie problemu PT
        
        ArrayList<String> gamesArray = DataConverter.teamArrayToFootbalGamesArray(teamsArray);      
        
        ArrayList<ArrayList<Object>> dataForScheduleTableRound1 = PrepareDataForSolutionTableRound1(gamesArray, teamsArray, sl.getVariables());
        ArrayList<ArrayList<Object>> dataForScheduleTableRound2 = PrepareDataForSolutionTableRound2(gamesArray, teamsArray, sl.getVariables());
        
        InsertDataToTable(dataForScheduleTableRound1, solutionTableRound1Model);
        InsertDataToTable(dataForScheduleTableRound2, solutionTableRound2Model);
    }
    
    /**
     * Przygotowanie danych rudny 1.
     * Metoda prywatna pozwalająca na przygotowanie danych do tabeli rozwiązań dla
     * pierwszej rundy rozgrywek.
     * @param gamesArray Lista rozgrywek (wszystkich meczy)
     * @param teamsArray Lista zespołów biorących udział w turnieju
     * @param solutionVariables Rozwiązanie w postaci zmiennych z JaCoPa
     * @return Rozwiązanie w postaci pozwalającej na wrzucenie do tabeli
     */
    private static ArrayList<ArrayList<Object>> PrepareDataForSolutionTableRound1(ArrayList<String> gamesArray, ArrayList<FootballTeam> teamsArray, Var[] solutionVariables)
    {
        ArrayList<ArrayList<Object>> table = new ArrayList<>();
        //String v = solutionVars[10].dom().toString(); //Wartość zmiennej
        for(String game : gamesArray)
        {
            ArrayList<Object> tableRow = new ArrayList<>(); // Kolejność czas - boisko - spotkanie
            tableRow.add(game); // Rozgrywane spotkanie
            for(Var solutionVar : solutionVariables)
            {
                if(solutionVar.id.contains(game))
                {
                    if(solutionVar.id.contains("boisko("))  // Wyznaczenie boiska
                    {
                        int fieldID = Integer.parseInt(solutionVar.dom().toString());
                        tableRow.add(teamsArray.get(fieldID-1).getFieldName());
                    }
                    else if(solutionVar.id.contains("czas("))   // Wyznaczenie czasu
                    {
                        
                        Integer day = Integer.valueOf(solutionVar.dom().toString());
                        Integer week = day / 7 + 1;
                        tableRow.add("Tydz: "+week+"   Dzień: "+day);
                    }
                }
            }
            table.add(tableRow);
        }
        
        return table;
    }
    
    /**
     * Przygotowanie danych rudny 2.
     * Metoda prywatna pozwalająca na przygotowanie danych do tabeli rozwiązań dla
     * pierwszej rundy rozgrywek.
     * @param gamesArray Lista rozgrywek (wszystkich meczy)
     * @param teamsArray Lista zespołów biorących udział w turnieju
     * @param solutionVariables Rozwiązanie w postaci zmiennych z JaCoPa
     * @return Rozwiązanie w postaci pozwalającej na wrzucenie do tabeli
     */
    private static ArrayList<ArrayList<Object>> PrepareDataForSolutionTableRound2(ArrayList<String> gamesArray, ArrayList<FootballTeam> teamsArray, Var[] solutionVariables)
    {
        ArrayList<ArrayList<Object>> table = new ArrayList<>();
        //String v = solutionVars[10].dom().toString(); //Wartość zmiennej
        for(String game : gamesArray)
        {
            ArrayList<Object> tableRow = new ArrayList<>(); // Kolejność czas - boisko - spotkanie
            tableRow.add(game); // Rozgrywane spotkanie
            for(Var solutionVar : solutionVariables)
            {
                if(solutionVar.id.contains(game))
                {
                    if(solutionVar.id.contains("boiskoRewanz("))  // Wyznaczenie boiska
                    {
                        int fieldID = Integer.parseInt(solutionVar.dom().toString());
                        tableRow.add(teamsArray.get(fieldID-1).getFieldName());
                    }
                    else if(solutionVar.id.contains("czasRewanz("))   // Wyznaczenie czasu
                    {
                        
                        Integer day = Integer.valueOf(solutionVar.dom().toString());
                        Integer week = day / 7 + 1;
                        tableRow.add("Tydz: "+week+"   Dzień: "+day);
//                        Integer day = Integer.valueOf(solutionVar.dom().toString())/48;
//                        Double hour = (double)(Integer.valueOf(solutionVar.dom().toString())%48)/2;
//                        tableRow.add("d: "+day.toString()+"; h: "+hour.toString());
                    }
                }
            }
            table.add(tableRow);
        }
        
        return table;
    }
    
    /**
     * Wrzucenie danych do tabeli w GUI.
     * Metoda pozwala na umieszczenie danych pochodzących z JaCoPa w tabeli
     * umieszczonej w GUI.
     * @param data Dane do tabeli
     * @param model Model tabeli do której zostaną wrzucone dane
     */
    private static void InsertDataToTable(ArrayList<ArrayList<Object>> data, DefaultTableModel model)
    {
        for(ArrayList<Object> row : data)
            model.addRow(new Object[] {row.get(0), row.get(1), row.get(2)});
    }
    
    /**
     * Szczegółowe dane problemu planowania.
     * Metoda pozwalająca na wyznaczenie szczegółów rozwiązywania problemu planowania
     * rozgrywek.
     * @param search Metoda poszukiwań JaCoPa
     * @return Szczegółowe rozwiązanie problemu planowania
     */
    public static String PlanningTaskGetSolutionDetails(Search search)
    {
        StringBuilder buf = new StringBuilder();
        
        buf.append("Węzły:\t\t").append(search.getNodes()).append("\n");
        buf.append("Decyzje:\t\t").append(search.getDecisions()).append("\n");
        buf.append("Złe decyzje:\t\t").append(search.getWrongDecisions()).append("\n");
        buf.append("Nawroty:\t\t").append(search.getBacktracks()).append("\n");
        buf.append("Max głębokość:\t\t").append(search.getMaximumDepth()).append("\n\n");
        
        return buf.toString();
    }
    // ********************** CONVERTER FOR PLANNING TASK ********************** //
}
